package com.sunricher.telinkblemeshlib.models;

import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import com.sunricher.telinkblemeshlib.MeshCommand;
import com.sunricher.telinkblemeshlib.MeshManager;
import com.sunricher.telinkblemeshlib.db.UartDaliDeviceManager;
import com.sunricher.telinkblemeshlib.util.HexUtil;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class UartDaliManager {

    private UartDaliManager.Callback callback;
    private UartDaliManager.DataCallback dataCallback;

    /// bleShortAddress, GatewayData
    private HashMap<Integer, GatewayData> uartGatewayDatas = new HashMap<>();
    private Handler handler = new Handler(Looper.getMainLooper());

    public static UartDaliManager getInstance() {
        return SingleHolder.instance;
    }

    private UartDaliManager() {
        super();
    }

    private static class SingleHolder {
        private static UartDaliManager instance = new UartDaliManager();
    }

    public abstract static class Callback {

        public void didExecuteCommandOK(int daliAddress, int gatewayAddress, int cmdType, int cmd) {
        }

        public void didExecuteCommandFailed(int daliAddress, int gatewayAddress, int reason, int cmdType, int cmd) {
        }

        public void didUpdateDeviceList(List<UartDaliDevice> devices, int gatewayAddress) {
        }

        public void didDiscoverEnd(int gatewayAddress, int reason) {
        }

        public void didGetDeviceGroups(List<Integer> groups, int daliAddress, int gatewayAddress) {
        }

        public void didAddDeviceToGroup(int daliAddress, int gatewayAddress) {
        }

        public void didRemoveDeviceFromGroup(int daliAddress, int gatewayAddress) {
        }

        public void didGetDeviceSceneValue(HashMap<String, Object> sceneValue, int daliAddress, int gatewayAddress) {
        }

        public void didUpdateDeviceSceneValue(int daliAddress, int gatewayAddress) {
        }

        public void didGetDeviceActualDataPoints(HashMap<String, Object> dataPoints, int daliAddress, int gatewayAddress) {
        }

        public void didGetDeviceConfigMinLevel(int minLevel, int gatewayAddress, int daliAddress) {
        }

        public void didGetDeviceConfigMaxLevel(int maxLevel, int gatewayAddress, int daliAddress) {
        }

        public void didGetDeviceConfigMinCct(int minCct, int gatewayAddress, int daliAddress) {
        }

        public void didGetDeviceConfigMaxCct(int maxCct, int gatewayAddress, int daliAddress) {
        }

        public void didGetDeviceConfigMinPhysicalCct(int minCct, int gatewayAddress, int daliAddress) {
        }

        public void didGetDeviceConfigMaxPhysicalCct(int maxCct, int gatewayAddress, int daliAddress) {
        }

        public void didGetDeviceConfigSystemFailureState(HashMap<String, Object> dataPoints, int gatewayAddress, int daliAddress) {
        }

        public void didGetDeviceConfigPowerOnState(HashMap<String, Object> dataPoints, int gatewayAddress, int daliAddress) {
        }

        public void didGetDeviceConfigFadeTime(int fadeTime, int fadeRate, int gatewayAddress, int daliAddress) {
        }
    }

    public abstract class DataCallback {

        public void didReceiveData(byte[] data) {
        }

    }

    public void setCallback(Callback callback) {
        this.callback = callback;
    }

    public void setDataCallback(DataCallback dataCallback) {
        this.dataCallback = dataCallback;
    }

    public List<UartDaliDevice> getExistDevices(int gatewayAddress, Context context) {
        return UartDaliDeviceManager.getInstance(context).getDevices(gatewayAddress);
    }

    /// Discover old devices, add new devices.
    public void discoverDevices(int gatewayAddress, Context context) {
        UartDaliDeviceManager.getInstance(context).deleteAll(gatewayAddress);

        // Don't send terminate cmd first.
        // MeshCommand terminate = MeshCommand.UartDali.terminateDiscovering(gatewayAddress);
        // MeshManager.getInstance().send(terminate);
        MeshCommand discover = MeshCommand.UartDali.discoverDevice(gatewayAddress, MeshCommand.UartDali.DISCOVER_WITHOUT_SHORT_ADDRESS_SHALL_REACT);
        MeshManager.getInstance().send(discover);
    }

    public void stopDiscoverDevices(int gatewayAddress) {
        MeshCommand cmd = MeshCommand.UartDali.terminateDiscovering(gatewayAddress);
        MeshManager.getInstance().send(cmd);
    }

    public void changeDeviceTypeManually(UartDaliDevice device, String newDeviceType, Context context) {
        device.setDeviceType(newDeviceType);
        device.setDataPoints(UartDaliDevice.getDefaultDataPoints(newDeviceType));
        UartDaliDeviceManager.getInstance(context).insertOrUpdate(device);
    }

    public void addNewDeviceManually(UartDaliDevice device, Context context) {
        UartDaliDeviceManager.getInstance(context).insertOrUpdate(device);
    }

    public void deleteDeviceManually(UartDaliDevice device, Context context) {
        UartDaliDeviceManager.getInstance(context).delete(device.getDaliAddress(), device.getGatewayAddress());
    }

    public void deleteAllDeviceManually(int gatewayAddress, Context context) {
        UartDaliDeviceManager.getInstance(context).deleteAll(gatewayAddress);
    }

    /*
    public void resetDevice(UartDaliDevice device, Context context) {
        UartDaliDeviceManager.getInstance(context).delete(device.getDaliAddress(), device.getGatewayAddress());
        MeshCommand cmd = MeshCommand.UartDali.configDevice(device.getGatewayAddress(), device.getDaliAddress(), MeshCommand.UartDali.CONFIG_RESET);
        MeshManager.getInstance().send(cmd);
    }

    public void resetAllDevices(int gatewayAddress, Context context) {
        UartDaliDeviceManager.getInstance(context).deleteAll(gatewayAddress);
        MeshCommand cmd = MeshCommand.UartDali.configDevice(gatewayAddress, 0xFF, MeshCommand.UartDali.CONFIG_RESET);
        MeshManager.getInstance().send(cmd);
    }
     */

    /// If you want to control a group, you need to set daliAddress as `groupId | 0x80`.
    /// If you want to control all devices, you need to set daliAddress as `0xFF`.
    public void updateDataPoints(int gatewayAddress, int daliAddress, HashMap<String, Object> dataPoints) {
        if (dataPoints == null || dataPoints.isEmpty()) {
            return;
        }

        try {

            if (dataPoints.containsKey("ON_OFF")) {
                boolean isOn = (boolean) dataPoints.get("ON_OFF");
                MeshCommand cmd;
                if (isOn) {
                    cmd = MeshCommand.UartDali.controlDevice(gatewayAddress, daliAddress, MeshCommand.UartDali.CONTROL_GO_TO_LAST_LEVEL);
                } else {
                    cmd = MeshCommand.UartDali.controlDevice(gatewayAddress, daliAddress, MeshCommand.UartDali.CONTROL_OFF);
                }
                MeshManager.getInstance().send(cmd);
            }

            if (dataPoints.containsKey("BRIGHTNESS")) {
                int brightness = (int) dataPoints.get("BRIGHTNESS");
                byte[] values = new byte[]{(byte) brightness};
                MeshCommand cmd = MeshCommand.UartDali.controlDevice(gatewayAddress, daliAddress, MeshCommand.UartDali.CONTROL_DIRECT_ARC_POWER_CONTROL, values);
                MeshManager.getInstance().send(cmd);
            }

            if (dataPoints.containsKey("COLOR_TEMPERATURE")) {
                int cct = (int) dataPoints.get("COLOR_TEMPERATURE");
                byte[] values = new byte[]{
                        (byte) ((cct >> 8) & 0xFF),
                        (byte) (cct & 0xFF)
                };
                MeshCommand cmd = MeshCommand.UartDali.controlDevice(gatewayAddress, daliAddress, MeshCommand.UartDali.CONTROL_ACTIVATE_CCT, values);
                MeshManager.getInstance().send(cmd);
            }

            if (dataPoints.containsKey("X") && dataPoints.containsKey("Y")) {
                int x = (int) dataPoints.get("X");
                int y = (int) dataPoints.get("Y");
                byte[] values = new byte[]{
                        (byte) ((x >> 8) & 0xFF),
                        (byte) (x & 0xFF),
                        (byte) ((y >> 8) & 0xFF),
                        (byte) (y & 0xFF),
                };
                MeshCommand cmd = MeshCommand.UartDali.controlDevice(gatewayAddress, daliAddress, MeshCommand.UartDali.CONTROL_ACTIVATE_XY, values);
                MeshManager.getInstance().send(cmd);
            }

            if (dataPoints.containsKey("RED") && dataPoints.containsKey("GREEN") && dataPoints.containsKey("BLUE")) {
                int r = (int) dataPoints.get("RED");
                int g = (int) dataPoints.get("GREEN");
                int b = (int) dataPoints.get("BLUE");
                int w = 0xFF;
                if (dataPoints.containsKey("WHITE")) {
                    w = (int) dataPoints.get("WHITE");
                }
                int a = 0xFF;
                if (dataPoints.containsKey("AMBER")) {
                    a = (int) dataPoints.get("AMBER");
                }
                byte[] values = new byte[]{
                        (byte) r, (byte) g, (byte) b, (byte) w, (byte) a, (byte) 0xFF
                };
                MeshCommand cmd = MeshCommand.UartDali.controlDevice(gatewayAddress, daliAddress, MeshCommand.UartDali.CONTROL_ACTIVATE_RGBWAF, values);
                MeshManager.getInstance().send(cmd);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void getDeviceGroups(int gatewayAddress, int daliAddress, int group) {
        int query = group <= 7 ? MeshCommand.UartDali.QUERY_GROUPS0_7 : MeshCommand.UartDali.QUERY_GROUPS8_15;
        MeshCommand cmd = MeshCommand.UartDali.queryDevice(gatewayAddress, daliAddress, query);
        MeshManager.getInstance().send(cmd);
    }

    public void addDeviceToGroup(int gatewayAddress, int daliAddress, int group) {
        byte[] values = new byte[]{(byte) group};
        MeshCommand cmd = MeshCommand.UartDali.configDevice(gatewayAddress, daliAddress, MeshCommand.UartDali.CONFIG_ADD_TO_GROUP, values);
        MeshManager.getInstance().send(cmd);
    }

    public void removeDeviceFromGroup(int gatewayAddress, int daliAddress, int group) {
        byte[] values = new byte[]{(byte) group};
        MeshCommand cmd = MeshCommand.UartDali.configDevice(gatewayAddress, daliAddress, MeshCommand.UartDali.CONFIG_REMOVE_FROM_GROUP, values);
        MeshManager.getInstance().send(cmd);
    }

    public void executeScene(int gatewayAddress, int scene) {
        executeScene(gatewayAddress, 0xFF, scene);
    }

    public void executeScene(int gatewayAddress, int daliAddress, int scene) {
        byte[] values = new byte[]{(byte) scene};
        MeshCommand cmd = MeshCommand.UartDali.controlDevice(gatewayAddress, daliAddress, MeshCommand.UartDali.CONTROL_GO_TO_SCENE, values);
        MeshManager.getInstance().send(cmd);
    }

    public void getDeviceSceneValue(int gatewayAddress, int daliAddress, int scene) {
        byte[] values = new byte[]{(byte) scene};
        MeshCommand cmd = MeshCommand.UartDali.queryDevice(gatewayAddress, daliAddress, MeshCommand.UartDali.QUERY_SCENE_VALUE, values);
        MeshManager.getInstance().send(cmd);
    }

    public void setDeviceSceneValue(int gatewayAddress, int daliAddress, int scene, HashMap<String, Object> dataPoints, String deviceType) {

        int brightness = 0xFF;
        if (dataPoints.containsKey("BRIGHTNESS")) {
            brightness = (int) dataPoints.get("BRIGHTNESS");
        }

        switch (deviceType) {
            case UartDaliDevice.DEVICE_TYPE_DT6:
                byte[] values = new byte[]{
                        (byte) scene, (byte) brightness,
                };
                MeshCommand cmd = MeshCommand.UartDali.configDevice(gatewayAddress, daliAddress, MeshCommand.UartDali.CONFIG_SET_SCENE_DT6, values);
                MeshManager.getInstance().send(cmd);
                break;

            case UartDaliDevice.DEVICE_TYPE_DT8_CCT:
                int cct = 0xFFFF;
                if (dataPoints.containsKey("COLOR_TEMPERATURE")) {
                    cct = (int) dataPoints.get("COLOR_TEMPERATURE");
                }
                int c0 = (byte) ((cct >> 8) & 0xFF);
                int c1 = (byte) (cct & 0xFF);
                byte[] cctValues = new byte[]{(byte) scene, (byte) brightness, (byte) c0, (byte) c1};
                MeshCommand cctCmd = MeshCommand.UartDali.configDevice(gatewayAddress, daliAddress, MeshCommand.UartDali.CONFIG_SET_SCENE_DT8_CCT, cctValues);
                MeshManager.getInstance().send(cctCmd);
                break;

            case UartDaliDevice.DEVICE_TYPE_DT8_XY:
                int x = 0xFFFF;
                int y = 0xFFFF;
                if (dataPoints.containsKey("X")) {
                    x = (int) dataPoints.get("X");
                }
                if (dataPoints.containsKey("Y")) {
                    y = (int) dataPoints.get("Y");
                }
                int x0 = (byte) ((x >> 8) & 0xFF);
                int x1 = (byte) (x & 0xFF);
                int y0 = (byte) ((y >> 8) & 0xFF);
                int y1 = (byte) (y & 0xFF);
                byte[] xyValues = new byte[]{
                        (byte) scene, (byte) brightness, (byte) x0, (byte) x1, (byte) y0, (byte) y1,
                };
                MeshCommand xyCmd = MeshCommand.UartDali.configDevice(gatewayAddress, daliAddress, MeshCommand.UartDali.CONFIG_SET_SCENE_DT8_XY, xyValues);
                MeshManager.getInstance().send(xyCmd);
                break;

            case UartDaliDevice.DEVICE_TYPE_DT8_RGBW:
            case UartDaliDevice.DEVICE_TYPE_DT8_RGBWA:
                int red = 0xFF;
                int green = 0xFF;
                int blue = 0xFF;
                int white = 0xFF;
                int amber = 0xFF;
                if (dataPoints.containsKey("RED")) {
                    red = (int) dataPoints.get("RED");
                }
                if (dataPoints.containsKey("GREEN")) {
                    green = (int) dataPoints.get("GREEN");
                }
                if (dataPoints.containsKey("BLUE")) {
                    blue = (int) dataPoints.get("BLUE");
                }
                if (dataPoints.containsKey("WHITE")) {
                    white = (int) dataPoints.get("WHITE");
                }
                if (dataPoints.containsKey("AMBER")) {
                    amber = (int) dataPoints.get("AMBER");
                }
                byte[] rgbValues = new byte[]{
                        (byte) brightness,
                        (byte) red, (byte) green, (byte) blue, (byte) white, (byte) amber, (byte) 0xFF
                };
                MeshCommand rgbCmd = MeshCommand.UartDali.setSceneDt8Rgb(gatewayAddress, daliAddress, scene, rgbValues);
                MeshManager.getInstance().send(rgbCmd);
                break;

            default:
                break;
        }
    }

    public void getDeviceActualDataPoints(int gatewayAddress, int daliAddress) {
        MeshCommand command = MeshCommand.UartDali.queryDevice(gatewayAddress, daliAddress, MeshCommand.UartDali.QUERY_ACTUAL_LEVEL);
        MeshManager.getInstance().send(command);
    }

    // MARK: - Config

    /// Will send query minLevel and maxLevel
    public void getLevelRange(int gatewayAddress, int daliAddress) {
        MeshCommand minCommand = MeshCommand.UartDali.queryDevice(gatewayAddress, daliAddress, MeshCommand.UartDali.QUERY_MIN_LEVEL);
        MeshCommand maxCommand = MeshCommand.UartDali.queryDevice(gatewayAddress, daliAddress, MeshCommand.UartDali.QUERY_MAX_LEVEL);
        ArrayList<MeshCommand> commands = new ArrayList<>();
        commands.add(minCommand);
        commands.add(maxCommand);
        MeshManager.getInstance().sendCommands(commands, 500);
    }

    public void getCctRange(int gatewayAddress, int daliAddress) {
        MeshCommand cctWarmest = MeshCommand.UartDali.queryDevice(gatewayAddress, daliAddress, MeshCommand.UartDali.QUERY_CCT_WARMEST);
        MeshCommand cctCoolest = MeshCommand.UartDali.queryDevice(gatewayAddress, daliAddress, MeshCommand.UartDali.QUERY_CCT_COOLEST);
        ArrayList<MeshCommand> commands = new ArrayList<>();
        commands.add(cctWarmest);
        commands.add(cctCoolest);
        MeshManager.getInstance().sendCommands(commands, 500);
    }

    public void getCctPhysicalRange(int gatewayAddress, int daliAddress) {
        MeshCommand cctWarmest = MeshCommand.UartDali.queryDevice(gatewayAddress, daliAddress, MeshCommand.UartDali.QUERY_PHYSICAL_WARMEST);
        MeshCommand cctCoolest = MeshCommand.UartDali.queryDevice(gatewayAddress, daliAddress, MeshCommand.UartDali.QUERY_PHYSICAL_COOLEST);
        ArrayList<MeshCommand> commands = new ArrayList<>();
        commands.add(cctWarmest);
        commands.add(cctCoolest);
        MeshManager.getInstance().sendCommands(commands, 500);
    }

    public void getSystemFailureState(int gatewayAddress, int daliAddress) {
        MeshCommand command = MeshCommand.UartDali.queryDevice(gatewayAddress, daliAddress, MeshCommand.UartDali.QUERY_SYSTEM_FAILURE_LEVEL);
        MeshManager.getInstance().send(command);
    }

    public void getPowerOnState(int gatewayAddress, int daliAddress) {
        MeshCommand command = MeshCommand.UartDali.queryDevice(gatewayAddress, daliAddress, MeshCommand.UartDali.QUERY_POWER_ON_LEVEL);
        MeshManager.getInstance().send(command);
    }

    public void getFadeTimeAndFadeRate(int gatewayAddress, int daliAddress) {
        MeshCommand command = MeshCommand.UartDali.queryDevice(gatewayAddress, daliAddress, MeshCommand.UartDali.QUERY_FADE_TIME_OR_FADE_RATE);
        MeshManager.getInstance().send(command);
    }

    public void setLevelRange(int gatewayAddress, int daliAddress, int minValue, int maxValue) {
        byte[] minValues = new byte[]{(byte) (minValue & 0xFF)};
        byte[] maxValues = new byte[]{(byte) (maxValue & 0xFF)};
        MeshCommand minCommand = MeshCommand.UartDali.configDevice(gatewayAddress, daliAddress, MeshCommand.UartDali.CONFIG_SET_MIN, minValues);
        MeshCommand maxCommand = MeshCommand.UartDali.configDevice(gatewayAddress, daliAddress, MeshCommand.UartDali.CONFIG_SET_MAX, maxValues);
        ArrayList<MeshCommand> commands = new ArrayList<>();
        commands.add(minCommand);
        commands.add(maxCommand);
        MeshManager.getInstance().sendCommands(commands, 500);
    }

    public void setCctRange(int gatewayAddress, int daliAddress, int minValue, int maxValue) {
        byte[] minValues = new byte[]{(byte) ((minValue >> 8) & 0xFF), (byte) (minValue & 0xFF)};
        byte[] maxValues = new byte[]{(byte) ((maxValue >> 8) & 0xFF), (byte) (maxValue & 0xFF)};
        MeshCommand minCommand = MeshCommand.UartDali.configDevice(gatewayAddress, daliAddress, MeshCommand.UartDali.CONFIG_SET_CCT_WARMEST, minValues);
        MeshCommand maxCommand = MeshCommand.UartDali.configDevice(gatewayAddress, daliAddress, MeshCommand.UartDali.CONFIG_SET_CCT_COOLEST, maxValues);
        ArrayList<MeshCommand> commands = new ArrayList<>();
        commands.add(minCommand);
        commands.add(maxCommand);
        MeshManager.getInstance().sendCommands(commands, 500);
    }

    public void setCctPhysicalRange(int gatewayAddress, int daliAddress, int minValue, int maxValue) {
        byte[] minValues = new byte[]{(byte) ((minValue >> 8) & 0xFF), (byte) (minValue & 0xFF)};
        byte[] maxValues = new byte[]{(byte) ((maxValue >> 8) & 0xFF), (byte) (maxValue & 0xFF)};
        MeshCommand minCommand = MeshCommand.UartDali.configDevice(gatewayAddress, daliAddress, MeshCommand.UartDali.CONFIG_SET_CCT_PHYSICAL_WARMEST, minValues);
        MeshCommand maxCommand = MeshCommand.UartDali.configDevice(gatewayAddress, daliAddress, MeshCommand.UartDali.CONFIG_SET_CCT_PHYSICAL_COOLEST, maxValues);
        ArrayList<MeshCommand> commands = new ArrayList<>();
        commands.add(minCommand);
        commands.add(maxCommand);
        MeshManager.getInstance().sendCommands(commands, 500);
    }

    public void setSystemFailureState(int gatewayAddress, int daliAddress, HashMap<String, Object> dataPoints, String deviceType) {
        switch (deviceType) {
            case UartDaliDevice.DEVICE_TYPE_DT6: {
                int brightness = 0xFF;
                if (dataPoints.containsKey("BRIGHTNESS")) {
                    brightness = (int) dataPoints.get("BRIGHTNESS");
                }
                byte[] values = new byte[]{(byte) (brightness & 0xFF)};
                MeshCommand command = MeshCommand.UartDali.configDevice(gatewayAddress, daliAddress, MeshCommand.UartDali.CONFIG_SET_SYSTEM_FAIL_DT6, values);
                MeshManager.getInstance().send(command);
                break;
            }

            case UartDaliDevice.DEVICE_TYPE_DT8_CCT: {
                int brightness = 0xFF;
                if (dataPoints.containsKey("BRIGHTNESS")) {
                    brightness = (int) dataPoints.get("BRIGHTNESS");
                }
                int c1 = 0xFF;
                int c2 = 0xFF;
                if (dataPoints.containsKey("COLOR_TEMPERATURE")) {
                    int cct = (int) dataPoints.get("COLOR_TEMPERATURE");
                    c1 = (cct >> 8) & 0xFF;
                    c2 = cct & 0xFF;
                }
                byte[] values = new byte[]{
                        (byte) (brightness & 0xFF),
                        (byte) (c1 & 0xFF), (byte) (c2 & 0xFF)
                };
                MeshCommand command = MeshCommand.UartDali.configDevice(gatewayAddress, daliAddress, MeshCommand.UartDali.CONFIG_SET_SYSTEM_FAIL_DT8_CCT, values);
                MeshManager.getInstance().send(command);
                break;
            }

            case UartDaliDevice.DEVICE_TYPE_DT8_XY: {
                int brightness = 0xFF;
                if (dataPoints.containsKey("BRIGHTNESS")) {
                    brightness = (int) dataPoints.get("BRIGHTNESS");
                }
                int x0 = 0xFF;
                int x1 = 0xFF;
                int y0 = 0xFF;
                int y1 = 0xFF;
                if (dataPoints.containsKey("X")) {
                    int x = (int) dataPoints.get("X");
                    x0 = (x >> 8) & 0xFF;
                    x1 = x & 0xFF;
                }
                if (dataPoints.containsKey("Y")) {
                    int y = (int) dataPoints.get("Y");
                    y0 = (y >> 8) & 0xFF;
                    y1 = y & 0xFF;
                }
                byte[] values = new byte[]{
                        (byte) (brightness & 0xFF),
                        (byte) (x0 & 0xFF), (byte) (x1 & 0xFF),
                        (byte) (y0 & 0xFF), (byte) (y1 & 0xFF),
                };
                MeshCommand command = MeshCommand.UartDali.configDevice(gatewayAddress, daliAddress, MeshCommand.UartDali.CONFIG_SET_SYSTEM_FAIL_DT8_XY, values);
                MeshManager.getInstance().send(command);
                break;
            }

            case UartDaliDevice.DEVICE_TYPE_DT8_RGBW:
            case UartDaliDevice.DEVICE_TYPE_DT8_RGBWA: {
                int brightness = 0xFF;
                if (dataPoints.containsKey("BRIGHTNESS")) {
                    brightness = (int) dataPoints.get("BRIGHTNESS");
                }
                int red = 0xFF;
                int green = 0xFF;
                int blue = 0xFF;
                int white = 0xFF;
                int amber = 0xFF;
                if (dataPoints.containsKey("RED")) {
                    red = (int) dataPoints.get("RED");
                }
                if (dataPoints.containsKey("GREEN")) {
                    green = (int) dataPoints.get("GREEN");
                }
                if (dataPoints.containsKey("BLUE")) {
                    blue = (int) dataPoints.get("BLUE");
                }
                if (dataPoints.containsKey("WHITE")) {
                    white = (int) dataPoints.get("WHITE");
                }
                if (dataPoints.containsKey("AMBER")) {
                    amber = (int) dataPoints.get("AMBER");
                }
                byte[] values = new byte[]{
                        (byte) (brightness & 0xFF),
                        (byte) (red & 0xFF), (byte) (green & 0xFF), (byte) (blue & 0xFF),
                        (byte) (white & 0xFF), (byte) (amber & 0xFF), (byte) 0xFF
                };
                MeshCommand command = MeshCommand.UartDali.setSystemFailDt8Rgb(gatewayAddress, daliAddress, values);
                MeshManager.getInstance().send(command);
                break;
            }
        }
    }

    public void setPowerOnState(int gatewayAddress, int daliAddress, HashMap<String, Object> dataPoints, String deviceType) {
        switch (deviceType) {
            case UartDaliDevice.DEVICE_TYPE_DT6: {
                int brightness = 0xFF;
                if (dataPoints.containsKey("BRIGHTNESS")) {
                    brightness = (int) dataPoints.get("BRIGHTNESS");
                }
                byte[] values = new byte[]{(byte) (brightness & 0xFF)};
                MeshCommand command = MeshCommand.UartDali.configDevice(gatewayAddress, daliAddress, MeshCommand.UartDali.CONFIG_SET_POWER_ON_DT6, values);
                MeshManager.getInstance().send(command);
                break;
            }

            case UartDaliDevice.DEVICE_TYPE_DT8_CCT: {
                int brightness = 0xFF;
                if (dataPoints.containsKey("BRIGHTNESS")) {
                    brightness = (int) dataPoints.get("BRIGHTNESS");
                }
                int c1 = 0xFF;
                int c2 = 0xFF;
                if (dataPoints.containsKey("COLOR_TEMPERATURE")) {
                    int cct = (int) dataPoints.get("COLOR_TEMPERATURE");
                    c1 = (cct >> 8) & 0xFF;
                    c2 = cct & 0xFF;
                }
                byte[] values = new byte[]{
                        (byte) (brightness & 0xFF),
                        (byte) (c1 & 0xFF), (byte) (c2 & 0xFF)
                };
                MeshCommand command = MeshCommand.UartDali.configDevice(gatewayAddress, daliAddress, MeshCommand.UartDali.CONFIG_SET_POWER_ON_DT8_CCT, values);
                MeshManager.getInstance().send(command);
                break;
            }

            case UartDaliDevice.DEVICE_TYPE_DT8_XY: {
                int brightness = 0xFF;
                if (dataPoints.containsKey("BRIGHTNESS")) {
                    brightness = (int) dataPoints.get("BRIGHTNESS");
                }
                int x0 = 0xFF;
                int x1 = 0xFF;
                int y0 = 0xFF;
                int y1 = 0xFF;
                if (dataPoints.containsKey("X")) {
                    int x = (int) dataPoints.get("X");
                    x0 = (x >> 8) & 0xFF;
                    x1 = x & 0xFF;
                }
                if (dataPoints.containsKey("Y")) {
                    int y = (int) dataPoints.get("Y");
                    y0 = (y >> 8) & 0xFF;
                    y1 = y & 0xFF;
                }
                byte[] values = new byte[]{
                        (byte) (brightness & 0xFF),
                        (byte) (x0 & 0xFF), (byte) (x1 & 0xFF),
                        (byte) (y0 & 0xFF), (byte) (y1 & 0xFF),
                };
                MeshCommand command = MeshCommand.UartDali.configDevice(gatewayAddress, daliAddress, MeshCommand.UartDali.CONFIG_SET_POWER_ON_DT8_XY, values);
                MeshManager.getInstance().send(command);
                break;
            }

            case UartDaliDevice.DEVICE_TYPE_DT8_RGBW:
            case UartDaliDevice.DEVICE_TYPE_DT8_RGBWA: {
                int brightness = 0xFF;
                if (dataPoints.containsKey("BRIGHTNESS")) {
                    brightness = (int) dataPoints.get("BRIGHTNESS");
                }
                int red = 0xFF;
                int green = 0xFF;
                int blue = 0xFF;
                int white = 0xFF;
                int amber = 0xFF;
                if (dataPoints.containsKey("RED")) {
                    red = (int) dataPoints.get("RED");
                }
                if (dataPoints.containsKey("GREEN")) {
                    green = (int) dataPoints.get("GREEN");
                }
                if (dataPoints.containsKey("BLUE")) {
                    blue = (int) dataPoints.get("BLUE");
                }
                if (dataPoints.containsKey("WHITE")) {
                    white = (int) dataPoints.get("WHITE");
                }
                if (dataPoints.containsKey("AMBER")) {
                    amber = (int) dataPoints.get("AMBER");
                }
                byte[] values = new byte[]{
                        (byte) (brightness & 0xFF),
                        (byte) (red & 0xFF), (byte) (green & 0xFF), (byte) (blue & 0xFF),
                        (byte) (white & 0xFF), (byte) (amber & 0xFF), (byte) 0xFF
                };
                MeshCommand command = MeshCommand.UartDali.setPowerOnDt8Rgb(gatewayAddress, daliAddress, values);
                MeshManager.getInstance().send(command);
                break;
            }
        }
    }

    public void setFadeTime(int gatewayAddress, int daliAddress, int value) {
        byte[] values = new byte[]{ (byte) (value & 0xFF) };
        MeshCommand command = MeshCommand.UartDali.configDevice(gatewayAddress, daliAddress, MeshCommand.UartDali.CONFIG_SET_FADE_TIME, values);
        MeshManager.getInstance().send(command);
    }

    public void setFadeRate(int gatewayAddress, int daliAddress, int value) {
        byte[] values = new byte[]{ (byte) (value & 0xFF) };
        MeshCommand command = MeshCommand.UartDali.configDevice(gatewayAddress, daliAddress, MeshCommand.UartDali.CONFIG_SET_FADE_RATE, values);
        MeshManager.getInstance().send(command);
    }

    public static class GatewayData {

        public static final int RESPONSE_TAG_DISCOVER_DT6 = 0x5AA0;
        public static final int RESPONSE_TAG_DISCOVER_DT8_XY = 0x5AA1;
        public static final int RESPONSE_TAG_DISCOVER_DT8_CCT = 0x5AA2;
        public static final int RESPONSE_TAG_DISCOVER_DT8_RGB = 0x5AA3;
        public static final int RESPONSE_TAG_DISCOVER_END = 0x5AAF;

        public static final int RESPONSE_TAG_COMMAND_OK = 0x5A80;
        public static final int RESPONSE_TAG_COMMAND_FAILED = 0x5A81;

        public static final int RESPONSE_TAG_COMMAND_FULL = 0x5A8E;
        public static final int RESPONSE_TAG_COMMAND_ALLOC_WAIT = 0x5A8F;

        public static final int RESPONSE_TAG_GROUPS0_7 = 0x5A11;
        public static final int RESPONSE_TAG_GROUPS8_15 = 0x5A12;

        public static final int RESPONSE_TAG_SCEEN_VALUE_DT6 = 0x5A3C;
        public static final int RESPONSE_TAG_SCENE_VALUE_DT8_XY = 0x5A3D;
        public static final int RESPONSE_TAG_SCENE_VALUE_DT8_CCT = 0x5A3E;
        public static final int RESPONSE_TAG_SCENE_VALUE_DT8_RGBWAF = 0x5A3F;

        public static final int RESPONSE_TAG_QUERY_STATUS = 0x5A00;
        public static final int RESPONSE_TAG_QUERY_ACTUAL_LEVEL_DT6 = 0x5A30;
        public static final int RESPONSE_TAG_QUERY_ACTUAL_LEVEL_DT8_XY = 0x5A31;
        public static final int RESPONSE_TAG_QUERY_ACTUAL_LEVEL_DT8_CCT = 0x5A32;
        public static final int RESPONSE_TAG_QUERY_ACTUAL_LEVEL_DT8_RGBWAF = 0x5A33;

        public static final int RESPONSE_TAG_QUERY_MIN_LEVEL = 0x5A0F;
        public static final int RESPONSE_TAG_QUERY_MAX_LEVEL = 0x5A0E;

        public static final int RESPONSE_TAG_QUERY_MIN_CCT = 0x5A18;
        public static final int RESPONSE_TAG_QUERY_MAX_CCT = 0x5A16;

        public static final int RESPONSE_TAG_QUERY_PHYSICAL_MIN_CCT = 0x5A19;
        public static final int RESPONSE_TAG_QUERY_PHYSICAL_MAX_CCT = 0x5A17;

        public static final int RESPONSE_TAG_QUERY_SYSTEM_FAILURE_STATE_DT6 = 0x5A38;
        public static final int RESPONSE_TAG_QUERY_SYSTEM_FAILURE_STATE_DT8_XY = 0x5A39;
        public static final int RESPONSE_TAG_QUERY_SYSTEM_FAILURE_STATE_DT8_CCT = 0x5A3A;
        public static final int RESPONSE_TAG_QUERY_SYSTEM_FAILURE_STATE_DT8_RGBWAF = 0x5A3B;
        public static final int RESPONSE_TAG_QUERY_POWER_ON_STATE_DT6 = 0x5A34;
        public static final int RESPONSE_TAG_QUERY_POWER_ON_STATE_DT8_XY = 0x5A35;
        public static final int RESPONSE_TAG_QUERY_POWER_ON_STATE_DT8_CCT = 0x5A36;
        public static final int RESPONSE_TAG_QUERY_POWER_ON_STATE_DT8_RGBWAF = 0x5A37;

        public static final int RESPONSE_TAG_QUERY_FADE_TIME_AND_FADE_RATE = 0x5A10;

        private int bleShortAddress;

        private byte[] data = new byte[10];

        public GatewayData(int bleShortAddress) {
            super();
            this.bleShortAddress = bleShortAddress;
        }

        public String getDataString(byte[] data) {
            return HexUtil.getStringByBytes(data);
        }

        public int getResponseTagValue(int data0, int data1) {
            return (data0 << 8) | data1;
        }

        public boolean isHeaderOK(int header) {
            return header == 0x5A;
        }

        public int getTypeValue(byte[] data) {
            return (int) data[1] & 0xFF;
        }

        public int getCommandValue(byte[] data) {
            return (int) data[2] & 0xFF;
        }
    }

    public void receive(MeshCommand cmd) {
        byte[] tempData = cmd.getUserData();
        byte[] data = new byte[tempData.length + 1];
        for (int i = 0; i < tempData.length; i++) {
            data[i + 1] = tempData[i];
        }
        data[0] = (byte) cmd.getParam();
        GatewayData gatewayData = new GatewayData(cmd.getSrc());
        gatewayData.data = data;
        System.out.println("uart data " + HexUtil.getStringByBytes(data));
        handler.post(() -> {
            if (dataCallback != null) {
                dataCallback.didReceiveData(data);
            }
        });

        // If this is a wireless command, return.
        if (handleWirelessCommand(cmd)) {
            return;
        }

        int responseTag = gatewayData.getResponseTagValue((int) data[0] & 0xFF, (int) data[1] & 0xFF);
        System.out.println("data tag " + responseTag);
        int bleShortAddress = cmd.getSrc();

        switch (responseTag) {

            case GatewayData.RESPONSE_TAG_DISCOVER_DT6:
                didFoundDevice((int) data[2] & 0xFF, bleShortAddress, UartDaliDevice.DEVICE_TYPE_DT6);
                break;

            case GatewayData.RESPONSE_TAG_DISCOVER_DT8_XY:
                didFoundDevice((int) data[2] & 0xFF, bleShortAddress, UartDaliDevice.DEVICE_TYPE_DT8_XY);
                break;

            case GatewayData.RESPONSE_TAG_DISCOVER_DT8_CCT:
                didFoundDevice((int) data[2] & 0xFF, bleShortAddress, UartDaliDevice.DEVICE_TYPE_DT8_CCT);
                break;

            case GatewayData.RESPONSE_TAG_DISCOVER_DT8_RGB:
                boolean isRgbwa = data[data.length - 1] == (byte) 0xC0;
                String deviceType = isRgbwa ? UartDaliDevice.DEVICE_TYPE_DT8_RGBWA : UartDaliDevice.DEVICE_TYPE_DT8_RGBW;
                didFoundDevice((int) data[2] & 0xFF, bleShortAddress, deviceType);
                break;

            case GatewayData.RESPONSE_TAG_DISCOVER_END:
                handleDiscoverEnd(bleShortAddress, (int) data[2] & 0xFF);
                break;

            case GatewayData.RESPONSE_TAG_COMMAND_OK:
                handleCommandOK((int) data[2] & 0xFF, bleShortAddress, (int) data[3] & 0xFF, (int) data[4] & 0xFF);
                break;

            case GatewayData.RESPONSE_TAG_COMMAND_FAILED:
                handleCommandFailed((int) data[2] & 0xFF, bleShortAddress, (int) data[3] & 0xFF, (int) data[4] & 0xFF, (int) data[5] & 0xFF);
                break;

            case GatewayData.RESPONSE_TAG_COMMAND_FULL:
                System.out.println("command full");
                break;

            case GatewayData.RESPONSE_TAG_COMMAND_ALLOC_WAIT:
                System.out.println("command alloc wait");
                break;

            case GatewayData.RESPONSE_TAG_GROUPS0_7:
                handleGroupsResponse0_7((int) data[2] & 0xFF, bleShortAddress, (int) data[3] & 0xFF);
                break;

            case GatewayData.RESPONSE_TAG_GROUPS8_15:
                handleGroupsResponse8_15((int) data[2] & 0xFF, bleShortAddress, (int) data[3] & 0xFF);
                break;

            case GatewayData.RESPONSE_TAG_SCEEN_VALUE_DT6:
                handleSceneValueDt6((int) data[2] & 0xFF, bleShortAddress, data);
                break;

            case GatewayData.RESPONSE_TAG_SCENE_VALUE_DT8_XY:
                handleSceneValueDt8Xy((int) data[2] & 0xFF, bleShortAddress, data);
                break;

            case GatewayData.RESPONSE_TAG_SCENE_VALUE_DT8_CCT:
                handleSceneValueDt8Cct((int) data[2] & 0xFF, bleShortAddress, data);
                break;

            case GatewayData.RESPONSE_TAG_SCENE_VALUE_DT8_RGBWAF:
                handleSceneValueDt8Rgbwaf((int) data[2] & 0xFF, bleShortAddress, data);
                break;

            case GatewayData.RESPONSE_TAG_QUERY_STATUS:
                handleQueryStatus((int) data[2] & 0xFF, bleShortAddress, data);
                break;

            case GatewayData.RESPONSE_TAG_QUERY_ACTUAL_LEVEL_DT6:
                handleQueryActualLevel((int) data[2] & 0xFF, bleShortAddress, data, UartDaliDevice.DEVICE_TYPE_DT6);
                break;

            case GatewayData.RESPONSE_TAG_QUERY_ACTUAL_LEVEL_DT8_XY:
                handleQueryActualLevel((int) data[2] & 0xFF, bleShortAddress, data, UartDaliDevice.DEVICE_TYPE_DT8_XY);
                break;

            case GatewayData.RESPONSE_TAG_QUERY_ACTUAL_LEVEL_DT8_CCT:
                handleQueryActualLevel((int) data[2] & 0xFF, bleShortAddress, data, UartDaliDevice.DEVICE_TYPE_DT8_CCT);
                break;

            case GatewayData.RESPONSE_TAG_QUERY_ACTUAL_LEVEL_DT8_RGBWAF:
                handleQueryActualLevel((int) data[2] & 0xFF, bleShortAddress, data, UartDaliDevice.DEVICE_TYPE_DT8_RGBWA);
                break;

            case GatewayData.RESPONSE_TAG_QUERY_MIN_LEVEL:
                handleQueryMinLevel((int) data[2] & 0xFF, bleShortAddress, data);
                break;
            case GatewayData.RESPONSE_TAG_QUERY_MAX_LEVEL:
                handleQueryMaxLevel((int) data[2] & 0xFF, bleShortAddress, data);
                break;

            case GatewayData.RESPONSE_TAG_QUERY_MIN_CCT:
                handleQueryMinCct((int) data[2] & 0xFF, bleShortAddress, data);
                break;
            case GatewayData.RESPONSE_TAG_QUERY_MAX_CCT:
                handleQueryMaxCct((int) data[2] & 0xFF, bleShortAddress, data);
                break;

            case GatewayData.RESPONSE_TAG_QUERY_PHYSICAL_MIN_CCT:
                handleQueryMinPhysicalCct((int) data[2] & 0xFF, bleShortAddress, data);
                break;
            case GatewayData.RESPONSE_TAG_QUERY_PHYSICAL_MAX_CCT:
                handleQueryMaxPhysicalCct((int) data[2] & 0xFF, bleShortAddress, data);
                break;

            case GatewayData.RESPONSE_TAG_QUERY_SYSTEM_FAILURE_STATE_DT6:
            case GatewayData.RESPONSE_TAG_QUERY_SYSTEM_FAILURE_STATE_DT8_XY:
            case GatewayData.RESPONSE_TAG_QUERY_SYSTEM_FAILURE_STATE_DT8_CCT:
            case GatewayData.RESPONSE_TAG_QUERY_SYSTEM_FAILURE_STATE_DT8_RGBWAF:
                handleQuerySystemFailureState((int) data[2] & 0xFF, bleShortAddress, data);
                break;
            case GatewayData.RESPONSE_TAG_QUERY_POWER_ON_STATE_DT6:
            case GatewayData.RESPONSE_TAG_QUERY_POWER_ON_STATE_DT8_XY:
            case GatewayData.RESPONSE_TAG_QUERY_POWER_ON_STATE_DT8_CCT:
            case GatewayData.RESPONSE_TAG_QUERY_POWER_ON_STATE_DT8_RGBWAF:
                handleQueryPowerOnState((int) data[2] & 0xFF, bleShortAddress, data);
                break;

            case GatewayData.RESPONSE_TAG_QUERY_FADE_TIME_AND_FADE_RATE:
                handleQueryFadeTimeAndFadeRate((int) data[2] & 0xFF, bleShortAddress, data);
                break;

            default:
                break;
        }
    }

    private UartDaliDeviceManager getDeviceManager() {
        return UartDaliDeviceManager.getInstance(MeshManager.getInstance().getApplication());
    }

    private void didFoundDevice(int daliAddress, int gatewayAddress, String deviceType) {
        UartDaliDevice device = new UartDaliDevice(daliAddress, gatewayAddress, deviceType);
        // If is new device
        UartDaliDeviceManager deviceManager = getDeviceManager();
        getDeviceManager().insertOrUpdate(device);
        List<UartDaliDevice> devices = deviceManager.getDevices(gatewayAddress);
        handler.post(() -> {
            if (callback != null) {
                callback.didUpdateDeviceList(devices, gatewayAddress);
            }
        });
    }

    private void handleDiscoverEnd(int gatewayAddress, int reasonValue) {
        handler.post(() -> {
            if (callback != null) {
                callback.didDiscoverEnd(gatewayAddress, reasonValue);
            }
        });
    }

    public static final int RESPONSE_COMMAND_TYPE_CONFIG = 0x01;
    public static final int RESPONSE_COMMAND_TYPE_CONTROL = 0x02;
    public static final int RESPONSE_COMMAND_TYPE_QUERY = 0x03;

    private void handleCommandOK(int daliAddress, int gatewayAddress, int typeValue, int cmdValue) {

        handler.post(() -> {
            if (callback != null) {
                callback.didExecuteCommandOK(daliAddress, gatewayAddress, typeValue, cmdValue);
            }
        });

        switch (typeValue) {
            case RESPONSE_COMMAND_TYPE_CONFIG:

                switch (cmdValue) {
                    case MeshCommand.UartDali.CONFIG_ADD_TO_GROUP:
                        handler.post(() -> {
                            if (callback != null) {
                                callback.didAddDeviceToGroup(daliAddress, gatewayAddress);
                            }
                        });
                        break;

                    case MeshCommand.UartDali.CONFIG_REMOVE_FROM_GROUP:
                        handler.post(() -> {
                            if (callback != null) {
                                callback.didRemoveDeviceFromGroup(daliAddress, gatewayAddress);
                            }
                        });
                        break;

                    case MeshCommand.UartDali.CONFIG_SET_SCENE_DT6:
                    case MeshCommand.UartDali.CONFIG_SET_SCENE_DT8_XY:
                    case MeshCommand.UartDali.CONFIG_SET_SCENE_DT8_CCT:
                        handler.post(() -> {
                            if (callback != null) {
                                callback.didUpdateDeviceSceneValue(daliAddress, gatewayAddress);
                            }
                        });
                        break;

                    default:
                        break;
                }
                break;

            default:
                break;
        }
    }

    private void handleCommandFailed(int daliAddress, int gatewayAddress, int typeValue, int cmdValue, int reasonValue) {
        handler.post(() -> {
            if (callback != null) {
                callback.didExecuteCommandFailed(daliAddress, gatewayAddress, reasonValue, typeValue, cmdValue);
            }
        });
    }

    private void handleGroupsResponse0_7(int daliAddress, int gatewayAddress, int value) {
        ArrayList<Integer> groups = new ArrayList<>();
        for (int i = 0; i <= 7; i++) {
            if (((0x01 << i) & value) > 0) {
                groups.add(i);
            }
        }
        handler.post(() -> {
            if (callback != null) {
                callback.didGetDeviceGroups(groups, daliAddress, gatewayAddress);
            }
        });
    }

    private void handleGroupsResponse8_15(int daliAddress, int gatewayAddress, int value) {
        ArrayList<Integer> groups = new ArrayList<>();
        for (int i = 0; i <= 7; i++) {
            if (((0x01 << i) & value) > 0) {
                groups.add(i + 8);
            }
        }
        handler.post(() -> {
            if (callback != null) {
                callback.didGetDeviceGroups(groups, daliAddress, gatewayAddress);
            }
        });
    }

    private void handleSceneValueDt6(int daliAddress, int gatewayAddress, byte[] data) {
        int level = (int) data[3] & 0xFF;
        boolean isOn = level > 0;
        HashMap<String, Object> value = new HashMap<>();
        value.put("ON_OFF", isOn);
        value.put("BRIGHTNESS", level);
        handler.post(() -> {
            if (callback != null) {
                callback.didGetDeviceSceneValue(value, daliAddress, gatewayAddress);
            }
        });
    }

    private void handleSceneValueDt8Xy(int daliAddress, int gatewayAddress, byte[] data) {
        int level = (int) data[3] & 0xFF;
        boolean isOn = level > 0;
        int x = ((int) data[4] & 0xFF << 8) | (int) data[5] & 0xFF;
        int y = ((int) data[6] & 0xFF << 8) | (int) data[7] & 0xFF;
        HashMap<String, Object> value = new HashMap<>();
        value.put("ON_OFF", isOn);
        value.put("BRIGHTNESS", level);
        value.put("X", x);
        value.put("Y", y);
        handler.post(() -> {
            if (callback != null) {
                callback.didGetDeviceSceneValue(value, daliAddress, gatewayAddress);
            }
        });
    }

    private void handleSceneValueDt8Cct(int daliAddress, int gatewayAddress, byte[] data) {
        int level = (int) data[3] & 0xFF;
        boolean isOn = level > 0;
        int cct = ((int) data[4] & 0xFF << 8) | (int) data[5] & 0xFF;
        HashMap<String, Object> value = new HashMap<>();
        value.put("ON_OFF", isOn);
        value.put("BRIGHTNESS", level);
        value.put("COLOR_TEMPERATURE", cct);
        handler.post(() -> {
            if (callback != null) {
                callback.didGetDeviceSceneValue(value, daliAddress, gatewayAddress);
            }
        });
    }

    private void handleSceneValueDt8Rgbwaf(int daliAddress, int gatewayAddress, byte[] data) {
        int level = (int) data[3] & 0xFF;
        boolean isOn = level > 0;
        int r = (int) data[4] & 0xFF;
        int g = (int) data[5] & 0xFF;
        int b = (int) data[6] & 0xFF;
        int w = (int) data[7] & 0xFF;
        int a = (int) data[8] & 0xFF;
        HashMap<String, Object> value = new HashMap<>();
        value.put("ON_OFF", isOn);
        value.put("BRIGHTNESS", level);
        value.put("RED", r);
        value.put("GREEN", g);
        value.put("BLUE", b);
        value.put("WHITE", w);
        value.put("AMBER", a);
        handler.post(() -> {
            if (callback != null) {
                callback.didGetDeviceSceneValue(value, daliAddress, gatewayAddress);
            }
        });
    }

    private void handleQueryStatus(int daliAddress, int gatewayAddress, byte[] data) {
        int value = (int) data[3] & 0xFF;
        boolean isControlGearFailure = (value & 0b0000_0001) > 0;
        boolean isLampFailure = (value & 0b0000_0010) > 0;
        boolean isLampOn = (value & 0b0000_0100) > 0;
        HashMap<String, Object> state = new HashMap<>();
        state.put("ON_OFF", isLampOn);
        state.put("LAMP_FAILURE", isLampFailure);
        state.put("CONTROL_GEAR_FAILURE", isControlGearFailure);
        handler.post(() -> {
            if (callback != null) {
                callback.didGetDeviceActualDataPoints(state, daliAddress, gatewayAddress);
            }
        });
    }

    private void handleQueryActualLevel(int daliAddress, int gatewayAddress, byte[] data, String deviceType) {
        int level = (int) data[3] & 0xFF;
        boolean isOn = level != 0;
        boolean isFailure = level == 0xFF;
        HashMap<String, Object> dataPoints = new HashMap<>();
        dataPoints.put("LAMP_FAILURE", isFailure);
        dataPoints.put("ON_OFF", isOn);
        dataPoints.put("BRIGHTNESS", level);
        dataPoints.put("DEVICE_TYPE", deviceType);


        switch (deviceType) {
            case UartDaliDevice.DEVICE_TYPE_DT6:
                break;

            case UartDaliDevice.DEVICE_TYPE_DT8_CCT:
                int c1 = (int) data[4] & 0xFF;
                int c2 = (int) data[5] & 0xFF;
                dataPoints.put("COLOR_TEMPERATURE", (c1 << 8) | c2);
                break;

            case UartDaliDevice.DEVICE_TYPE_DT8_XY:
                int x1 = (int) data[4] & 0xFF;
                int x2 = (int) data[5] & 0xFF;
                int y1 = (int) data[6] & 0xFF;
                int y2 = (int) data[7] & 0xFF;
                dataPoints.put("X", (x1 << 8) | x2);
                dataPoints.put("Y", (y1 << 8) | y2);
                break;

            case UartDaliDevice.DEVICE_TYPE_DT8_RGBW:
            case UartDaliDevice.DEVICE_TYPE_DT8_RGBWA:
                dataPoints.put("RED", (int) data[4] & 0xFF);
                dataPoints.put("GREEN", (int) data[5] & 0xFF);
                dataPoints.put("BLUE", (int) data[6] & 0xFF);
                dataPoints.put("WHITE", (int) data[7] & 0xFF);
                dataPoints.put("AMBER", (int) data[8] & 0xFF);
                break;

            default:
                break;
        }
        handler.post(() -> {
            if (callback != null) {
                callback.didGetDeviceActualDataPoints(dataPoints, daliAddress, gatewayAddress);
            }
        });
    }


    private void handleQueryMinLevel(int daliAddress, int gatewayAddress, byte[] data) {
        int level = (int) data[3] & 0xFF;
        if (!UartDaliDevice.levelRange.contains(level)) {
            level = UartDaliDevice.levelRange.getMinValue();
        }
        int finalLevel = level;
        handler.post(() -> {
            if (callback != null) {
                callback.didGetDeviceConfigMinLevel(finalLevel, gatewayAddress, daliAddress);
            }
        });
    }

    private void handleQueryMaxLevel(int daliAddress, int gatewayAddress, byte[] data) {
        int level = (int) data[3] & 0xFF;
        if (!UartDaliDevice.levelRange.contains(level)) {
            level = UartDaliDevice.levelRange.getMaxValue();
        }
        int finalLevel = level;
        handler.post(() -> {
            if (callback != null) {
                callback.didGetDeviceConfigMaxLevel(finalLevel, gatewayAddress, daliAddress);
            }
        });
    }

    private void handleQueryMinCct(int daliAddress, int gatewayAddress, byte[] data) {
        int c1 = ((int) data[3] & 0xFF) << 8;
        int c2 = (int) data[4] & 0xFF;
        int cct = c1 | c2;
        if (!UartDaliDevice.cctRange.contains(cct)) {
            cct = UartDaliDevice.cctRange.getMinValue();
        }
        int finalCct = cct;
        handler.post(() -> {
            if (callback != null) {
                callback.didGetDeviceConfigMinCct(finalCct, gatewayAddress, daliAddress);
            }
        });
    }

    private void handleQueryMaxCct(int daliAddress, int gatewayAddress, byte[] data) {
        int c1 = ((int) data[3] & 0xFF) << 8;
        int c2 = (int) data[4] & 0xFF;
        int cct = c1 | c2;
        if (!UartDaliDevice.cctRange.contains(cct)) {
            cct = UartDaliDevice.cctRange.getMaxValue();
        }
        int finalCct = cct;
        handler.post(() -> {
            if (callback != null) {
                callback.didGetDeviceConfigMaxCct(finalCct, gatewayAddress, daliAddress);
            }
        });
    }

    private void handleQueryMinPhysicalCct(int daliAddress, int gatewayAddress, byte[] data) {
        int c1 = ((int) data[3] & 0xFF) << 8;
        int c2 = (int) data[4] & 0xFF;
        int cct = c1 | c2;
        if (!UartDaliDevice.cctRange.contains(cct)) {
            cct = UartDaliDevice.cctRange.getMinValue();
        }
        int finalCct = cct;
        handler.post(() -> {
            if (callback != null) {
                callback.didGetDeviceConfigMinPhysicalCct(finalCct, gatewayAddress, daliAddress);
            }
        });
    }

    private void handleQueryMaxPhysicalCct(int daliAddress, int gatewayAddress, byte[] data) {
        int c1 = ((int) data[3] & 0xFF) << 8;
        int c2 = (int) data[4] & 0xFF;
        int cct = c1 | c2;
        if (!UartDaliDevice.cctRange.contains(cct)) {
            cct = UartDaliDevice.cctRange.getMaxValue();
        }
        int finalCct = cct;
        handler.post(() -> {
            if (callback != null) {
                callback.didGetDeviceConfigMaxPhysicalCct(finalCct, gatewayAddress, daliAddress);
            }
        });
    }

    private void handleQuerySystemFailureState(int daliAddress, int gatewayAddress, byte[] data) {
        int tag = (int) data[1] & 0xFF;
        HashMap<String, Object> dataPoints = new HashMap<>();
        int level = (int) data[3] & 0xFF;
        dataPoints.put("BRIGHTNESS", level);

        switch (tag) {
            case 0x38: // dt6
                break;

            case 0x39: // dt8 xy
            {
                int x1 = ((int) data[4] & 0xFF) << 8;
                int x2 = (int) data[5] & 0xFF;
                int x = x1 | x2;
                boolean xMask = x == UartDaliDevice.stateXyMask;
                int y1 = ((int) data[6] & 0xFF) << 8;
                int y2 = (int) data[7] & 0xFF;
                int y = y1 | y2;
                boolean yMask = y == UartDaliDevice.stateXyMask;
                dataPoints.put("X", x);
                dataPoints.put("Y", y);
                dataPoints.put("X_MASK", xMask);
                dataPoints.put("Y_MASK", yMask);
                break;
            }

            case 0x3A: // dt8 cct
            {
                int c1 = ((int) data[4] & 0xFF) << 8;
                int c2 = (int) data[5] & 0xFF;
                int cct = c1 | c2;
                boolean cctMask = cct == UartDaliDevice.stateCctMask;
                dataPoints.put("COLOR_TEMPERATURE", cct);
                dataPoints.put("COLOR_TEMPERATURE_MASK", cctMask);
                break;
            }

            case 0x3B: // dt8 rgbwaf
            {
                int red = (int) data[4] & 0xFF;
                int green = (int) data[5] & 0xFF;
                int blue = (int) data[6] & 0xFF;
                int white = (int) data[7] & 0xFF;
                int amber = (int) data[8] & 0xFF;
                dataPoints.put("RED", red);
                dataPoints.put("RED_MASK", red == UartDaliDevice.stateRgbwafMask);
                dataPoints.put("GREEN", green);
                dataPoints.put("GREEN_MASK", green == UartDaliDevice.stateRgbwafMask);
                dataPoints.put("BLUE", blue);
                dataPoints.put("BLUE_MASK", blue == UartDaliDevice.stateRgbwafMask);
                dataPoints.put("WHITE", white);
                dataPoints.put("WHITE_MASK", white == UartDaliDevice.stateRgbwafMask);
                dataPoints.put("AMBER", amber);
                dataPoints.put("AMBER_MASK", amber == UartDaliDevice.stateRgbwafMask);
                break;
            }

            default:
                break;
        }
        handler.post(() -> {
            if (callback != null) {
                callback.didGetDeviceConfigSystemFailureState(dataPoints, gatewayAddress, daliAddress);
            }
        });
    }

    private void handleQueryPowerOnState(int daliAddress, int gatewayAddress, byte[] data) {
        int tag = (int) data[1] & 0xFF;
        HashMap<String, Object> dataPoints = new HashMap<>();
        int level = (int) data[3] & 0xFF;
        dataPoints.put("BRIGHTNESS", level);

        switch (tag) {
            case 0x34: // dt6
                break;

            case 0x35: // dt8 xy
                int x1 = ((int) data[4] & 0xFF) << 8;
                int x2 = (int) data[5] & 0xFF;
                int x = x1 | x2;
                boolean xMask = x == UartDaliDevice.stateXyMask;
                int y1 = ((int) data[6] & 0xFF) << 8;
                int y2 = (int) data[7] & 0xFF;
                int y = y1 | y2;
                boolean yMask = y == UartDaliDevice.stateXyMask;
                dataPoints.put("X", x);
                dataPoints.put("Y", y);
                dataPoints.put("X_MASK", xMask);
                dataPoints.put("Y_MASK", yMask);
                break;

            case 0x36: // dt8 cct
                int c1 = ((int) data[4] & 0xFF) << 8;
                int c2 = (int) data[5] & 0xFF;
                int cct = c1 | c2;
                boolean cctMask = cct == UartDaliDevice.stateCctMask;
                dataPoints.put("COLOR_TEMPERATURE", cct);
                dataPoints.put("COLOR_TEMPERATURE_MASK", cctMask);
                break;

            case 0x37: // dt8 rgbwaf
                int red = (int) data[4] & 0xFF;
                int green = (int) data[5] & 0xFF;
                int blue = (int) data[6] & 0xFF;
                int white = (int) data[7] & 0xFF;
                int amber = (int) data[8] & 0xFF;
                dataPoints.put("RED", red);
                dataPoints.put("RED_MASK", red == UartDaliDevice.stateRgbwafMask);
                dataPoints.put("GREEN", green);
                dataPoints.put("GREEN_MASK", green == UartDaliDevice.stateRgbwafMask);
                dataPoints.put("BLUE", blue);
                dataPoints.put("BLUE_MASK", blue == UartDaliDevice.stateRgbwafMask);
                dataPoints.put("WHITE", white);
                dataPoints.put("WHITE_MASK", white == UartDaliDevice.stateRgbwafMask);
                dataPoints.put("AMBER", amber);
                dataPoints.put("AMBER_MASK", amber == UartDaliDevice.stateRgbwafMask);
                break;

            default:
                break;
        }
        handler.post(() -> {
            if (callback != null) {
                callback.didGetDeviceConfigPowerOnState(dataPoints, gatewayAddress, daliAddress);
            }
        });
    }

    private void handleQueryFadeTimeAndFadeRate(int daliAddress, int gatewayAddress, byte[] data) {
        int value = (int) data[3] & 0xFF;
        int fadeTime = (value >> 4) & 0x0F;
        int fadeRate = value & 0x0F;
        handler.post(() -> {
            if (callback != null) {
                callback.didGetDeviceConfigFadeTime(fadeTime, fadeRate, gatewayAddress, daliAddress);
            }
        });
    }

    /// If this is a wireless command, return true, otherwise return false.
    private boolean handleWirelessCommand(MeshCommand cmd) {
        if (cmd.getTag() != 0xEB || cmd.getVendorId() != 0x1102) {
            return false;
        }
        byte[] data = cmd.getUserData();
        int tag = (int) data[0] & 0xFF;
        switch (tag) {
            case 0x72: // Smart switches response
            {
                int count = (int) data[1] & 0xFF;
                int index = (int) data[2] & 0xFF;
                int value0 = ((int) data[6] & 0xFF) << 24;
                int value1 = ((int) data[5] & 0xFF) << 16;
                int value2 = ((int) data[4] & 0xFF) << 8;
                int value3 = (int) data[3] & 0xFF;
                int smartSwitchId = value0 | value1 | value2 | value3;
                String hexString = Integer.toHexString(smartSwitchId);
                Log.i("UartDaliManager", "DALI smart switches count " + count + ", index " + index + ", ID " + hexString);
                return true;
            }

            default:
                return false;
        }
    }
}
